package com.abel.bigwater.tlv.server

import com.abel.bigwater.tlv.model.*
import com.alibaba.fastjson.JSON
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelInboundHandlerAdapter
import io.netty.channel.socket.DatagramPacket
import org.slf4j.LoggerFactory
import java.net.InetSocketAddress

class TlvServerHandler : ChannelInboundHandlerAdapter() {

    private val log = LoggerFactory.getLogger(TlvServerHandler::class.java)

    override fun channelActive(ctx: ChannelHandlerContext?) {
        log.info("GOT connection from: ${ctx?.channel()?.remoteAddress()}")
    }

    override fun channelRead(ctx: ChannelHandlerContext?, msg: Any?) {
        when {
            msg is TlvDevPacket -> {
                processPacket(ctx, msg, ctx?.channel()?.remoteAddress() as InetSocketAddress)
            }

            msg is DatagramPacket -> {
                try {
                    val ba = ByteArray(msg.content().readableBytes())
                    msg.content().readBytes(ba)

                    val buf = ctx!!.alloc()!!.buffer()
                    buf.writeBytes(ba)

                    while (buf.readableBytes() > 0) {
                        log.info("UDP bytes: ${TlvHelper.toHexString(ba)}")

                        val p1 = TlvDevPacket()
                        p1.parse(buf)

                        processPacket(ctx, p1, msg.sender())
                    }
                } catch (ex: Exception) {
                    log.error("FAIL to parse UDP packet caused by ${ex.message}", ex)
                }
            }

            else -> {
                log.info("FAIL to parse caused by unknown msg: ${msg}")
            }
        }
    }

    private fun processPacket(ctx: ChannelHandlerContext?, p1: TlvDevPacket, sender: InetSocketAddress?) {
        log.info(JSON.toJSONString(p1, true))

        when (p1.body!!.command) {
            TlvCmdType.T1Online.cmd -> {
                val p2 = TlvPlatformPacket()
                p2.body = TlvDevBody(command = TlvCmdType.T1Online.reply, dataList = ArrayList())

                val d1 = TlvData(ResultCodeParam.TAG, resultCode = ResultCodeType.T0Success.id)
                p2.body?.dataList?.add(d1)

                val buf = ctx?.alloc()?.buffer()
                p2.fill(buf!!)

                // send back.
                try {
                    val dp = DatagramPacket(buf, sender)
                    ctx.writeAndFlush(dp)
                    log.info("Try to reply online udp: ${dp}")
                } catch (ex: Exception) {
                    log.info("Fail to reply online udp caused by ${ex.message}", ex)
                }
            }

            TlvCmdType.T3UpRealtime.cmd -> {
                val p2 = TlvPlatformPacket()
                p2.body = TlvDevBody(command = TlvCmdType.T3UpRealtime.reply, dataList = ArrayList())

                val d1 = TlvData(ResultCodeParam.TAG, resultCode = ResultCodeType.T0Success.id)
                p2.body?.dataList?.add(d1)

                val buf = ctx?.alloc()?.buffer()
                p2.fill(buf!!)

                // send back.
                try {
                    val dp = DatagramPacket(buf, sender)
                    ctx.writeAndFlush(dp)
                    log.info("Try to reply period udp: ${dp}")
                } catch (ex: Exception) {
                    log.info("Fail to reply period udp caused by ${ex.message}", ex)
                }

            }
        }
    }

    override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) {
        log.error("FAIL caused by ${cause?.message}", cause)
        ctx?.close()
    }
}